// Protothread for c++
#ifndef __PROTOTHREAD_H__
#define __PROTOTHREAD_H__

/** A lightweight, stackless thread. Override the Run() method and use
class LEDFlasher : public Protothread
{
    public:
        virtual bool Run();
};
bool LEDFlasher::Run()
{
    PT_BEGIN();
    while (1)
    {
        printf("1\n");
        PT_DELAY(100);
    }
    PT_END();
}
**/
class Protothread
{
public:
    static Protothread* M_pts[10];
    static int M_npt;
    Protothread();
    Protothread(const char * ptName);
    virtual ~Protothread() { }
    void Restart() { _ptLine = 0; }
    void Stop() { _ptLine = LineNumberInvalid; }
    bool IsRunning() { return _ptLine != LineNumberInvalid; }
    void OnTick();
    void PtOsDelay(unsigned long tick);
    void PtOsDelayResume();
    static void  OnTickAll();
    virtual bool Run() = 0;
protected:
    unsigned int  m_id;
    unsigned long m_delay;
    char   m_ptName[16];
    typedef unsigned short LineNumber;
    static const LineNumber LineNumberInvalid = (LineNumber)(-1);
    LineNumber _ptLine;
public:
    void *  indata;
    void *  outdata;
    unsigned char state;
};

#define PT_BEGIN() bool ptYielded = true; (void) ptYielded; switch (_ptLine) { case 0:
#define PT_END() default: ; } Stop(); return false;
#define PT_WAIT_UNTIL(condition) \
    do { _ptLine = __LINE__; case __LINE__: \
    if (!(condition)) return true; } while (0)
#define PT_WAIT_WHILE(condition) PT_WAIT_UNTIL(!(condition))
#define PT_WAIT_THREAD(child) PT_WAIT_WHILE((child).Run())
#define PT_SPAWN(child) \
    do { (child).Restart(); PT_WAIT_THREAD(child); } while (0)
#define PT_RESTART() do { Restart(); return true; } while (0)
#define PT_EXIT() do { Stop(); return false; } while (0)
#define PT_YIELD() \
    do { ptYielded = false; _ptLine = __LINE__; case __LINE__: \
    if (!ptYielded) return true; } while (0)

#define PT_YIELD_UNTIL(condition) \
    do { ptYielded = false; _ptLine = __LINE__; case __LINE__: \
    if (!ptYielded || !(condition)) return true; } while (0)

#define PT_DELAY(v)				\
  do {						\
    m_delay = v;			\
    PT_WAIT_UNTIL(m_delay == 0);		\
  } while(0)

#endif